/*
	Copyright (c) 2008 TPSi
	Alex Iskander
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/

#include "BindableObject.h"
#include "Bindable.h"
#include "Selectors/NameSelector.h"
#include <algorithm>
#include <vector>
#include <iostream>

using namespace Bind;
using std::string;

BindableObject::BindableObject()
{
	this->autoDeleteProperties = false;
}

BindableObject::~BindableObject()
{
	/* Disconnect from parents */
	//look through parent list
	
	//get iterator
	std::multimap<std::string, BindableObject *>::iterator pi;
	
	//loop
	for (pi = parents.begin(); pi != parents.end(); pi++)
	{
		//if this is the parent we are looking for, tell it we are being
		//destroyed.
		(*pi).second->destroyed(this);
	}
	
	/* Loop through listeners */
	//get an iterator
	std::vector<Bindable *>::iterator it;
	
	//set iterator
	it = this->listeners.begin();
	
	//while we are not at the end of our listeners
	while(it != this->listeners.end())
	{
		//ensure we are moving through the list
		size_t size = this->listeners.size();
		
		//inform the listener that we have been destroyed
		(*it)->destroyed(this);
		
		//if the listener has not been removed
		if (this->listeners.size() == size)
		{
			//remove it
			this->listeners.erase(it);
		}
		
		//return to the beginning of the list (which should now be the next item).
		it = this->listeners.begin();
	}

	//clear listener list
	this->listeners.clear();
	
	
	/* Loop through objects we are listening to */
	
	//start at the beginning
	it = this->listeningTo.begin();
	
	//and keep going until we reach the end
	while(it != this->listeningTo.end())
	{
		//get the property we are listening to
		Bindable *b = (*it);
		
		//remove the listener
		b->removeListener(this);
		
		//return to the beginning (which should be the next item).
		it = this->listeningTo.begin();
	}
	
	//clear the listeningTo list, just in case. Pointless line, really.
	this->listeningTo.clear();
	
	/* unbindChild all children */
	//prepare an iterator
	std::set<BindableObject *>::iterator child;
	
	//start at the beginning
	child = children.begin();
	
	//keep going until the end
	while (child != children.end())
	{
		//get the child
		BindableObject *b = *child;
		
		//unbind
		this->unbindChild(b);
		
		//return to the beginning.
		child = children.begin(); 
	}
	
	//destroy properties if needed.
	if (this->autoDeleteProperties)
	{
		std::map<Bindable *, int>::iterator property;
		for (property = this->propertySet.begin(); property != this->propertySet.end(); property++)
		{
			delete (*property).first;
		}
	}
}

std::string BindableObject::getTypeName()
{
	return "(generic)";
}

void BindableObject::bind(BindableObject *bindTo, int depth)
{

	/* 
	 Very simple
	 Some things to note: if depth != 0, we \e should be receiving this call
	 from a parent. As such, any notifications about changes in bindTo will
	 propagate through \e the \e parent. Therefore, we are \e not required to
	 add a listener in any other circumstance.
	 */
	//add to boundTo array if depth is 0 (a direct bind)
	if (depth == 0)
	{
		//add to array
		this->boundTo.push_back(bindTo);
		
		//add a listener
		bindTo->addListener(this);
	}
	
	//loop through properties
	std::map<string, Bindable *>::iterator it;
	for (it = this->properties.begin(); it != this->properties.end(); it++)
	{
		//get the property to bind to
		Bindable *p = (*it).second;
		
		// don't set the property if its value is already higher-priority.
		// note that this is for performance only; the selector will select the
		// right one regardless.
		if (p->getDepth() < depth && p->getDepth() >= 0) continue;
		
		//get the name of the field to attempt to bind the property to
		Selector *bTo = p->bindTo();
		
		/* 
		 At one point, we tried to auto-bind based on field name.
		 
		 This caused more trouble (automatic bindings galore!) than it was worth,
		 and has as such been removed.
		 */
		
		//prepare the possible property value
		Bindable *propertyToBindTo = NULL;
		
		//if we have a selector
		if (bTo) 
		{
			//use it
			propertyToBindTo = bTo->get(this);
		}
		
		//bind to property
		if (propertyToBindTo)
		{
			p->set(*propertyToBindTo, false);
			p->setDepth(depth);
		}		
	}
	
	/* Loop through children */
	std::set<BindableObject *>::iterator child;
	for (child = this->children.begin(); child != this->children.end(); child++)
	{
		(*child)->bind(bindTo, depth + 1);
	}
}

void BindableObject::recalculateBindings(int recursionDepth)
{
	/*
	 Very Simple.
	 
	 Loop through properties. For each one, recalculate.
	 
	 In addition, call recalculateBindings for each child if recursion depth
	 is greater than 0 (with a recursion depth of recursionDepth -1)
	 */
	
	std::map<std::string, Bindable *>::iterator it;
	for (it = this->properties.begin(); it != this->properties.end(); it++)
	{
		//get the property to bind to
		Bindable *p = (*it).second;
		
		//get the name of the field to attempt to bind the property to
		Selector *bTo = p->bindTo();
		
		/* 
		 If we don't have a selector, create a default one.
		 This practice may be disable-able in future through a boolean called
		 "auto-bind".
		 */
		//keep a value so we know we should delete it if we create it.
		Bindable *propertyToBindTo = NULL;
		
		//if we don't have a selector
		if (bTo) 
		{
			//use the selector
			propertyToBindTo = bTo->get(this);
		}
		
		//bind to property
		if (propertyToBindTo)
		{
			p->set(*propertyToBindTo, false);
			
			//reset depth.
			p->setDepth(-1);
		}
	}
	
	if (recursionDepth < 0) recursionDepth = 50;
	if (recursionDepth == 0) return;
	
	std::set<BindableObject *>::iterator child = this->children.begin();
	while (child != this->children.end())
	{
		(*child)->recalculateBindings(recursionDepth - 1);
		child++;
	}
	
}

void BindableObject::unbind(BindableObject *bindTo, bool recalculate)
{
	/*
	 Very simple (a catch-phrase).
	 
	 Remove (all instances) from bound list.
	 Recalculate bindings.
	 Loop through children, call unbind.
	 */
	
	//step one - remove from bound list
	std::vector<BindableObject *>::iterator boundTo;
	boundTo = std::find(this->boundTo.begin(), this->boundTo.end(), bindTo);
	while (boundTo != this->boundTo.end())
	{
		(*boundTo)->removeListener(this);
		
		boundTo = this->boundTo.erase(boundTo);
		
		boundTo = find(boundTo, this->boundTo.end(), bindTo);
	}
	
	//step 2: recalculate bindings if recalculate is true
	if (recalculate)
	{
		this->recalculateBindings(-1);
		
		//step 3: alert children
		std::set<BindableObject *>::iterator child = this->children.begin();
		while (child != this->children.end())
		{
			(*child)->recalculateBindings(-1);
			child++;
		}		
	}
	
}

void BindableObject::set(Bindable &property, bool copy)
{
	BindableObject *b = dynamic_cast<BindableObject *> (&property);
	
	if (b) this->bind(b);
	
	/* Copy selector */
	if (copy)
		this->bindTo(property.bindTo(), false);
}

void BindableObject::bake()
{
	/* Remove bindings */
	std::vector<BindableObject *>::iterator bt = this->boundTo.begin();
	while (bt != this->boundTo.end())
	{
		this->unbind((*bt), false);
		bt = this->boundTo.begin();
	}
	
	
	/* Bake properties */
	std::map<string, Bindable *>::iterator it;
	for (it = this->properties.begin(); it != this->properties.end(); it++)
	{
		(*it).second->bake();
	}
}

Bindable *BindableObject::get(std::string name, int recursionDepth, bool skipSelf)
{
	/* If this object has the property, return it */
	if (!skipSelf && this->properties.count(name) > 0)
	{
		return this->properties[name];
	}
	
	/* Otherwise, see if we should recurse */
	// if recursionDepth == 0, there should be no recursion
	if (recursionDepth == 0) return NULL;
	
	//if it is less than 0, we should recurse for MAX_RECUSION_DEPTH (for now,
	//just an arbitrary 50, not some constant).
	if (recursionDepth < 0) recursionDepth = 50;
	
	
	/* Recurse Bound To */
	//prepare an iterator
	std::vector<BindableObject *>::iterator boundTo;
	
	//start at the beginning
	boundTo = this->boundTo.begin();
	
	//keep going until the end
	while (boundTo != this->boundTo.end())
	{
		//get the property
		Bindable *p = (*boundTo)->get(name, recursionDepth - 1);
		
		//if something was returned, return it
		if (p) return p;
		
		//otherwise, continue
		boundTo++;
	}
	
	
	/* Recurse Parents */
	//prepare an iterator
	std::multimap<std::string, BindableObject *>::iterator parent;
	
	//start at the beginning
	parent = this->parents.begin();
	
	//keep going until the end
	while (parent != this->parents.end())
	{
		//get the property
		Bindable *p = (*parent).second->get(name, recursionDepth - 1);
		
		//if something was returned, return it
		if (p) return p;
		
		//otherwise, continue
		parent++;
	}
	
	//return NULL on failure.
	return NULL;
}

void BindableObject::bindChild(BindableObject *child, std::string name)
{
	//if we already have the child
	if (this->children.count(child) > 0)
	{
		//don't bother to remove it from our list; it will be overwritten (sets
		//only allow one copy of an element).
		
		//instead, inform CHILD to remove the item
		child->removeParent(child);
	}
	
	//if the name is blank
	if (name == "")
	{
		//use the type name
		name = this->getTypeName();
		
		//if that is unspecified, use "(generic)"
		//note: it should NOT be unspecified (if it was, it would be "generic")
		//but we NEVER want "".
		if (name == "") name = "(generic)";
	}
	
	//add as parent to the child
	child->addParent(name, this);
	
	//add to our children list
	this->children.insert(child);
	
	//recalculate bindings (we may want to upgrade this later for better perf.)
	this->recalculateBindings();
	

}

void BindableObject::unbindChild(BindableObject *child)
{
	//don't do anything if it isn't a child
	if (this->children.count(child) == 0) return;
	
	//alert the child that we are erasing it
	child->removeParent(this);
	
	//erase the child
	this->children.erase(child);

}

void BindableObject::addParent(std::string name, BindableObject *parent)
{
	//add to parent list
	parents.insert(std::pair<std::string, BindableObject *>(name, parent));
}

void BindableObject::removeParent(BindableObject *parent)
{
	//look through parent list
	
	//get iterator
	std::multimap<std::string, BindableObject *>::iterator it;
	
	//loop
	for (it = parents.begin(); it != parents.end(); it++)
	{
		//if this is the parent we are looking for
		if ((*it).second == parent)
		{
			//erase
			parents.erase(it);
			
			//cease processing
			break;
		}
	}
	
	
}

void BindableObject::destroyed(Bindable *property)
{
	/*
	 Very simple.
	 
	 First, we determine if \e property is a child. If it is, then we remove
	 it from our list of children.
	 
	 Next, we determine if we are bound to the item. If we are, we unbind 
	 from it.
	 
	 Finally, we double check our listeners (because I'm paranoid about these
	 things ever since there were some leaks and crashes regarding them).
	 */
	
	/* Is it a bindable? */
	BindableObject *obj = dynamic_cast<BindableObject *> (property);

	if (obj)
	{
		//is it a child?
		std::set<BindableObject *>::iterator cit = children.find(obj);
		
		//if so
		if (cit != children.end())
		{
			//remove it from child array (don't worry about unbinding -- there
			//is nothing there)
			children.erase(cit);
		}
		
		//get iterator
		std::vector<BindableObject *>::iterator it;
		
		//is it in boundTo?
		it = find(boundTo.begin(), boundTo.end(), obj);
		
		//if so
		if (it != boundTo.end())
		{
			//unbind
			this->unbind((*it));
		}

	}

	//double-check removing listener
	property->removeListener(this);
	
	//double-check removing listeningTo
	std::vector<Bindable *>::iterator lt = this->listeningTo.begin();
	while (lt != this->listeningTo.begin())
	{
		if ((*lt) == property)
		{
			lt = this->listeningTo.erase(lt);
		} else lt++;
	}
	

}

bool BindableObject::isBindableObject()
{
	//this is a Bindable, so of course it is Bindable
	return true;
}

BindableObject *BindableObject::getBindableObject()
{
	//return this, as it \e is a Bindable.
	return this;
}

void BindableObject::addProperty(std::string name, Bindable *property)
{
	//set the property name
	property->setName(name);
	
	//set as parent
	property->setParent(this);
	
	//add the property
	this->properties[name] = property;
	
	//add to the set
	if (this->propertySet.count(property) > 0)
	{
		this->propertySet[property]++;
	}
	else
	{
		this->propertySet[property] = 1;
	}

}

void BindableObject::removeProperty(std::string name)
{
	//get property
	Bindable *property = this->properties[name];
	
	//remove
	property->setParent(NULL);
	
	//remove from map
	this->properties.erase(name);
	
	//if more than one exists
	if (this->propertySet.count(property) > 1)
	{
		//decrement
		this->propertySet[property]--;
	}
	else
	{
		//otherwise, remove all record.
		this->propertySet.erase(property);
	}
}
